﻿/*
    PerstSyncProvider
    Copyright (C) 2011 Mikolas Bingemer, MediFox GmbH

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
    MA 02110-1301  USA.
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Perst;

namespace MediFox.Synchronization.ClientServices.PerstSyncProvider
{
    public class PerstOfflineSchema : Persistent
    {

        [NonSerialized]
        private Dictionary<string, PropertyInfo[]> _entityProperties = new Dictionary<string, PropertyInfo[]>();
        
        [NonSerialized]
        private Dictionary<string, PropertyInfo[]> _entityKeyProperties = new Dictionary<string, PropertyInfo[]>();

        [NonSerialized]
        private Dictionary<string, PropertyInfo[]> _entityNonKeyProperties = new Dictionary<string, PropertyInfo[]>();
        
        private Dictionary<string, Type> _collections = new Dictionary<string, Type>();

        public void AddCollection<T>() where T : PerstOfflineEntity
        {
            Type entityType = typeof(T);
            //Logger.Log(System.Reflection.MethodBase.GetCurrentMethod(), entityType.FullName);

            if (GetEntityKeyProperties(entityType).Length == 0)
            {
                throw new ArgumentException(entityType.FullName + " must designate at least one key property");
            }

            _collections.Add(entityType.FullName, entityType);
        }

        public ReadOnlyCollection<Type> Collections
        {
            get
            {
                List<Type> typesList = new List<Type>(_collections.Values);
                ReadOnlyCollection<Type> typesCollection = new ReadOnlyCollection<Type>(typesList);
                return typesCollection;
            }
        }

        internal object[] GetPrimaryKeyValues(PerstOfflineEntity entity)
        {
            Type entityType = entity.GetType();
            //Logger.Log(System.Reflection.MethodBase.GetCurrentMethod(), entityType.FullName);

            PropertyInfo[] keyProperties = GetEntityKeyProperties(entityType);
            object[] keyValues = new object[keyProperties.Length];
            for (int i = 0; i < keyProperties.Length; i++)
            {
                PropertyInfo keyProperty = keyProperties[i];
                keyValues[i] = keyProperty.GetValue(entity, new object[0]);
            }

            return keyValues;
        }

        internal PropertyInfo[] GetEntityProperties(Type entityType)
        {
            //Logger.Log(System.Reflection.MethodBase.GetCurrentMethod(), entityType.FullName);

            PropertyInfo[] entityProperties;
            if (!_entityProperties.TryGetValue(entityType.FullName, out entityProperties))
            {
                entityProperties = (from property in entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                    where ((property.GetGetMethod() != null) && (property.GetSetMethod() != null)) && (property.DeclaringType == entityType)
                                    select property).ToArray<PropertyInfo>();

                _entityProperties.Add(entityType.FullName, entityProperties);
            }

            return entityProperties;
        }

        internal PropertyInfo[] GetEntityKeyProperties(Type entityType)
        {
            //Logger.Log(System.Reflection.MethodBase.GetCurrentMethod(), entityType.FullName);

            PropertyInfo[] entityKeyProperties;
            if (!_entityKeyProperties.TryGetValue(entityType.FullName, out entityKeyProperties))
            {
                entityKeyProperties = (from p in GetEntityProperties(entityType)
                                       where p.GetCustomAttributes(typeof(KeyAttribute), false).Length != 0
                                       select p).ToArray<PropertyInfo>();

                _entityKeyProperties.Add(entityType.FullName, entityKeyProperties);
            }

            return entityKeyProperties;
        }

        internal PropertyInfo[] GetEntityNonKeyProperties(Type entityType)
        {
            //Logger.Log(System.Reflection.MethodBase.GetCurrentMethod(), entityType.FullName);

            PropertyInfo[] entityNonKeyProperties;
            if (!_entityNonKeyProperties.TryGetValue(entityType.FullName, out entityNonKeyProperties))
            {
                entityNonKeyProperties = (from p in GetEntityProperties(entityType)
                                          where p.GetCustomAttributes(typeof(KeyAttribute), false).Length == 0
                                          select p).ToArray<PropertyInfo>();

                _entityNonKeyProperties.Add(entityType.FullName, entityNonKeyProperties);
            }

            return entityNonKeyProperties;
        }

    }
}
